using System;
using CombinatorialObjects;
using Salesman.Genetic;

namespace Salesman
{
    public class GeneticTSPAlgorithm : ITSPAlgorithm
    {
        private GAImpl _ga = new GAImpl();

        public event EventHandler<ReportProgressArgs> ReportProgress;

        private void InvokeReportProgress(ReportProgressArgs e)
        {
            EventHandler<ReportProgressArgs> progress = ReportProgress;
            if (progress != null) progress(this, e);
        }

        public GeneticTSPAlgorithm()
        {
            _ga.ReportProgress+=OnReport;
        }

        private void OnReport(object sender, ReportProgressArgs e)
        {
            InvokeReportProgress(e);
        }

        ~GeneticTSPAlgorithm()
        {
            _ga.ReportProgress -= ReportProgress;
        }
        public TSPSolution Solve(ITSPInstance problemInstance)
        {
            _ga.Instance = problemInstance;
            DateTime start = DateTime.Now;
            _ga.Go();
            TimeSpan dur = DateTime.Now - start;
            Genome genome;
            double fitness;
            _ga.GetBest(out genome, out fitness);

            var perm = genome as PermutationGenome;
            if (perm == null)
                throw new Exception();
            var solution = new TSPSolution(problemInstance, perm.Genes);
            InvokeReportProgress(new ReportProgressArgs(1, solution, new TSPLogEntry(string.Format("f {0} t {1}", solution.RouteLength(), dur), solution)));
            return solution;
        }
        public int Generations
        {
            get { return _ga.Generations; }
            set { _ga.Generations = value; }
        }
        public int PopulationSize
        {
            get { return _ga.PopulationSize; }
            set { _ga.PopulationSize = value; }
        }

        public double MutationRate
        {
            get { return _ga.MutationRate; }
            set { _ga.MutationRate = value; }
        }
        public double CrossoveRate
        {
            get { return _ga.CrossoverRate; }
            set { _ga.CrossoverRate = value; }
        }

        class GAImpl : GA
        {
            private ITSPInstance _instance;
            private TSPSolution _solution;
            private double _maxRoute;
            private int genomeSize;

            public event EventHandler<ReportProgressArgs> ReportProgress;

            private void InvokeReportProgress(ReportProgressArgs e)
            {
                EventHandler<ReportProgressArgs> progress = ReportProgress;
                if (progress != null) progress(this, e);
            }

            public GAImpl()
            {
                CrossoverRate = 1;
                Generations = 1000;
                PopulationSize = 100;
                MutationRate = 0.05;
            }

            protected override double Fitness(Genome genome)
            {
                var permG = genome as PermutationGenome;
                if (permG != null)
                {
                    _solution.Route = permG.Genes;
                    return 1- _solution.RouteLength()/_maxRoute;
                }
                else
                    return 0;
            }

            private int _currentGeneration;
            protected override void OnNextGeneration()
            {
                base.OnNextGeneration();
                _currentGeneration++;
                if (_currentGeneration % (Generations>>7) == 0)
                {
                    double fit;
                    Genome gen;
                    GetBest(out gen, out fit);
                    var permGen = gen as PermutationGenome;
                    if (permGen != null)
                    {
                        var solution = new TSPSolution(Instance, permGen.Genes);
                        InvokeReportProgress(new ReportProgressArgs((float)_currentGeneration / Generations,
                                                                    solution,
                                                                    new TSPLogEntry(string.Format("f={0}", fit), solution)));
                    }
                }
            }
            protected override void OnGo()
            {
                _currentGeneration = 0;
                base.OnGo();
            }
            protected override Genome CreateGenome()
            {
                return new PermutationGenome(genomeSize);
            }
            public new int Generations
            {
                get { return base.Generations; }
                set { base.Generations = value; }
            }
            public new int PopulationSize
            {
                get { return base.PopulationSize; }
                set { base.PopulationSize = value; }
            }
            public new double CrossoverRate
            {
                get { return base.CrossoverRate; }
                set { base.CrossoverRate = value; }
            }
            
            public ITSPInstance Instance
            {
                get { return _instance; }
                set
                {
                    _instance = value;
                    _solution = new TSPSolution(Instance);
                    _maxRoute = _solution.RouteLength() * 4;
                    genomeSize = Instance.CityCount;
                }
            }
        }
        public override string ToString()
        {
            return "Genetyczny";
        }
    }
}